#ifndef SCHEDULER_CC
#define SCHEDULER_CC

#include <iostream>
#include "Scheduler.h"
#include "Processor.cc"

using namespace std;

Scheduler::Scheduler(){
	
}

Scheduler::Scheduler(list<Process>* procList, int userProc, int numQueue, int numProcess, int numQuantum, int ageTime){
	processList = procList;
	userProcess = userProc;
	numQueues = numQueue;
	numProc = numProcess;
	numQuant = numQuantum;
	agingTime = ageTime;
	callScheduler(); 
	
}
Scheduler::~Scheduler(){
	// delete processList;
	// delete pq;
	// delete allProcessors;
	// delete waitingProcesses;
	// delete finishedProcesses;
	// delete SRTFwaiting;
	// delete priorityWaiting;
	// delete MLqueue;

}

void Scheduler::callScheduler(){
//	int temp = userProcess;
	
	pq = new priority_queue< Process, vector<Process>, less<vector<Process>::value_type> >();
	tempPQ = new priority_queue< Process, vector<Process>, less<vector<Process>::value_type> >();
	list<Process>::iterator i = processList->begin();
	

	
	while(i != processList->end()){
		
		pq->push(*i);
		cout<<*i<<endl;
		i++;
	}
	cout<<"HERE"<<endl;
	if (userProcess==1){
		HRN();
	}
	 if (userProcess==2){
		MFQS();
	 }
	// if (userProcess==3){
		// Priority();


}


void Scheduler::MFQS() {
	cout<<"Running MFQS..."<<endl;
	allProcessors = new Processor[numProc];
	// add the specified number of queues to the queue holder
	for (int lcv2 =0; lcv2<numProc; lcv2++){
		queue<Process>** queueHolder = new queue<Process>*[numQueues];
		Processor p;
		for (int lcv =0; lcv<numQueues; lcv++){
			MLqueue = new queue<Process>();
			queueHolder[lcv]=MLqueue;
			p.addQueueList(queueHolder, numQueues);
		}
		allProcessors[lcv2]=p;
	}
	tempPQ = new priority_queue< Process, vector<Process>, less<vector<Process>::value_type> >();
	
		int time =0;
	bool done = false;
	bool allDone = false;
	//NEW CHANGE to array of Process for Current process????????
	//Process currentProcess[numProc];
	
	//So current process gets set.
	for(int lcv = 0; lcv < numProc; lcv++){
		Process p;
		allProcessors[lcv].currentProcess = p;
		allProcessors[lcv].currentProcess.queueNumber = 6;
	}
	while (allDone == false){
		cout<<"***********************************************************"<<endl;
		cout<<"Time: "<<time<<" ";
		if (!pq->empty()){
			while (pq->top().arrival==time&&done == false){
				cout<<"Process "<<pq->top().PID<<" arrives. "<<endl;
				int least = 0;
				for (int lcv =0; lcv < numProc; lcv++){
					if(allProcessors[lcv].getNumProcesses()<allProcessors[least].getNumProcesses()){
					least = lcv;
					}
				}
				
				//waitingProcesses->push_back(pq->top());
				
				allProcessors[least].addPtoQ(pq->top());
				cout<<"Adding Process: "<< pq->top().PID<<" to Processor "<< least<<endl;

				if (pq->size()==1){
					pq->pop();
					done = true;
				}else{
					pq->pop();
				}	

			}
			
		}
		//Start new stuff.
		
		
		for(int lcv = 0; lcv < numProc; lcv++){
			//cout << allProcessors[lcv].multipleQueue[
			cout << allProcessors[lcv].getNumProcesses() << " processes in processor " << lcv << endl;
		    //if the specified Processor is not empty
			if(!allProcessors[lcv].isEmpty()){
				cout<<"Processor "<<lcv<<" is not empty."<<endl;
				//NEW
				if(allProcessors[lcv].multipleQueue[(numQueues - 1)]->size() > 0){
				//In last queue check all age
				queue<Process>* store = new queue<Process>;
					for(int i = 0; i < allProcessors[lcv].multipleQueue[(numQueues - 1)]->size();i++){
					//For every one check age
						if(allProcessors[lcv].multipleQueue[(numQueues - 1)]->front().age == agingTime){
						//hit aging time move up a queue.
							allProcessors[lcv].multipleQueue[(numQueues - 2)]->push(allProcessors[lcv].multipleQueue[(numQueues - 1)]->front());
							allProcessors[lcv].multipleQueue[(numQueues - 1)]->pop();
						}else{
						//keep in last queue not aged enough
						//AGE HERE???
							store->push(allProcessors[lcv].multipleQueue[(numQueues - 1)]->front());
							allProcessors[lcv].multipleQueue[(numQueues - 1)]->pop();
							store->front().age++;
						}
					}
					for(int i = 0; i < allProcessors[lcv].multipleQueue[(numQueues - 1)]->size();i++){
					//put back in last queue
					allProcessors[lcv].multipleQueue[(numQueues - 1)]->push(store->front());
					store->pop();
					}
				}
				//Set current processor by finding next one
				int topLevel = -1;
				for(int i = 0; i < numQueues;i++){
				//Go through queues find where they are
				if(allProcessors[lcv].multipleQueue[i]->size()>0){
					topLevel = i;
				}else{
				//empty?????
				}
				}
				if(allProcessors[lcv].currentProcess.queueNumber > topLevel){// || allProcessors[lcv]currentProcess.PID == -1){ maybe if we have a null process then just add one
				//Process with higher priority maybe one was added this wont happen often need to move current to end of current queue and put higher priority as current
				if(allProcessors[lcv].currentProcess.queueNumber == 6){
				//for first time set current process ???????????????????????
				allProcessors[lcv].currentProcess = allProcessors[lcv].multipleQueue[topLevel]->front();
				allProcessors[lcv].multipleQueue[topLevel]->pop();
				}else{
				allProcessors[lcv].multipleQueue[allProcessors[lcv].currentProcess.queueNumber]->push(allProcessors[lcv].currentProcess);
				allProcessors[lcv].currentProcess = allProcessors[lcv].multipleQueue[topLevel]->front();
				allProcessors[lcv].multipleQueue[topLevel]->pop();
				}
				}
				//we have current process as one that should be.
				
				if ((allProcessors[lcv].currentProcess.remainingTime==0)||(allProcessors[lcv].currentProcess.runTimeinQueue == numQuant)){ //process is either done, if not in last queue and hit time quant, or in last queue and hit age limit
				//	int currentQ = allProcessors[lcv].currentProcess.queueNumber;
					if(allProcessors[lcv].currentProcess.remainingTime ==0){
					//Current Process is finished
						cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" finishes. ";	
						allProcessors[lcv].currentProcess.setEndTime(time);  
						allProcessors[lcv].finishedProcesses->push_back(allProcessors[lcv].currentProcess);
						
						//NEW set current process to next one.
						Process p;
						p = allProcessors[lcv].getNextProcess();
						if(p.PID != -1){
						allProcessors[lcv].currentProcess = p;
						allProcessors[lcv].multipleQueue[p.queueNumber]->pop();
						}else{
						//Processor empty
						}
						
					}else if(allProcessors[lcv].currentProcess.runTimeinQueue == numQuant){
					// Quatum time reached move to next queue
					allProcessors[lcv].currentProcess.runTimeinQueue = 0;
					allProcessors[lcv].currentProcess.queueNumber = allProcessors[lcv].currentProcess.queueNumber + 1;
					allProcessors[lcv].multipleQueue[(allProcessors[lcv].currentProcess.queueNumber+1)]->push(allProcessors[lcv].currentProcess);
					Process p;
						p = allProcessors[lcv].getNextProcess();
						if(p.PID != -1){
						allProcessors[lcv].currentProcess = p;
						allProcessors[lcv].multipleQueue[p.queueNumber]->pop();
						}else{
						//Processor empty
						allDone = true;
						}
					
					}
				
						//get new currentProcess from either the waiting list for the current queue or the next queue
						/*
						cout << "Checking queues " << endl;
						if (allProcessors[lcv].multipleQueue[currentQ]->size()>0){// there are some processes in the waiting queue
						cout << "Same Queue " << endl;
							allProcessors[lcv].currentProcess = allProcessors[lcv].multipleQueue[currentQ]->front();
							allProcessors[lcv].multipleQueue[currentQ]->pop();
						
						}else{//there are no more waiting processes in the current queue
							//go through the rest of the queues and look for a waiting process
							//
							cout << "None in current queue check others " << endl;
							for (int i =currentQ+1; i<numQueues; i++){
								if (allProcessors[lcv].multipleQueue[i]->size()>0){
								cout << "Found Some Processes " << endl;
									allProcessors[lcv].currentProcess = allProcessors[lcv].multipleQueue[i]->front();
									allProcessors[lcv].multipleQueue[i]->pop();
								}else if (i==(numQueues-1)){// got to last queue without finding a process
									cout << "No More in processor" << endl;
									done = true; // no more waiting processes
								}
							}
							
						}
						
					//CHANGE below what if no processes?
					if(!done){
					allProcessors[lcv].currentProcess.startTime=time;
					cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" gets the CPU ";
					}
					*/
					allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime-1;
		cout<<"Remaining Time for process "<<allProcessors[lcv].currentProcess.PID<<": "<<allProcessors[lcv].currentProcess.remainingTime<<endl;
		allProcessors[lcv].currentProcess.runTime = allProcessors[lcv].currentProcess.runTime+1;
				}
				// else run
				
					
						cout<<"Not empty or done and not time quatum."<<endl;
				
				
			
				//add to current process.
				 
			
			/* if not empty - if current process is done or null
			say process is finished set stuff 
			get new process from queue if not in queue check other queue grab next one.
			otherwise done!
			
			
			*/
			
			}else{// the Processor does not have any waiting processes
				// NOT SURE when to check if other processor have processes??????????????
			//
			if ((allProcessors[lcv].currentProcess.remainingTime==0)||(allProcessors[lcv].currentProcess.runTimeinQueue == numQuant)){
				if(allProcessors[lcv].currentProcess.remainingTime ==0){
					//Current Process is finished
						cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" finishes. ";	
						allProcessors[lcv].currentProcess.setEndTime(time);  
						allProcessors[lcv].finishedProcesses->push_back(allProcessors[lcv].currentProcess);
						allDone = true;
						}
				else if(allProcessors[lcv].currentProcess.runTimeinQueue == numQuant){
					// Quatum time reached move to next queue
					allProcessors[lcv].currentProcess.runTimeinQueue = 0;
					allProcessors[lcv].currentProcess.queueNumber = allProcessors[lcv].currentProcess.queueNumber + 1;
					allProcessors[lcv].multipleQueue[(allProcessors[lcv].currentProcess.queueNumber+1)]->push(allProcessors[lcv].currentProcess);
					allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime-1;
		cout<<"Remaining Time for process "<<allProcessors[lcv].currentProcess.PID<<": "<<allProcessors[lcv].currentProcess.remainingTime<<endl;
		allProcessors[lcv].currentProcess.runTime = allProcessors[lcv].currentProcess.runTime+1;
					}
					
				}	
				}
			/*
				cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" finishes. ";
				allProcessors[lcv].currentProcess.setEndTime(time);
				allProcessors[lcv].finishedProcesses->push_back(allProcessors[lcv].currentProcess);
				
				if (done==true){
			*/		
					// There are no more processes in allProcessor[lcv]
					//check to see if any other processes have processes waiting and add it to that one
					
					/*
					bool swappedProcess = false;
					for (int lcv2 =0; lcv2<numProc; lcv2++){
						if ((lcv2 != lcv)&&(swappedProcess ==false)){
							if (allProcessors[lcv2].getNumWaitingProcesses()>0){

								cout<<"Adding Process "<<allProcessors[lcv2].multipleQueue[0]->front().PID<<" from Processor "<<lcv2 <<" to Processor "<<lcv<<endl;
								allProcessors[lcv].currentProcess =allProcessors[lcv2].multipleQueue[0]->front();
								allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime+1;
								allProcessors[lcv2].multipleQueue[0]->pop();
								swappedProcess= true;
							}
						}
					}
					*/
				/*
			
				*/
				/*
				}
				else{

						allProcessors[lcv].currentProcess.PID = -1;

				}
			
			}
		 }
		 if (allProcessors[lcv].currentProcess.remainingTime>0){
		 allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime-1;
		 cout<<"Remaining Time for process "<<allProcessors[lcv].currentProcess.PID<<": "<<allProcessors[lcv].currentProcess.remainingTime<<endl;
		 allProcessors[lcv].currentProcess.runTime = allProcessors[lcv].currentProcess.runTime+1;
		 }else{
			Process p;
			
			allProcessors[lcv].currentProcess = p;
		 }
		 
		}
		*/
		if(allProcessors[lcv].currentProcess.queueNumber < (numQueues -1)){
			allProcessors[lcv].currentProcess.runTimeinQueue = allProcessors[lcv].currentProcess.runTimeinQueue +1;
		}
		
		
		time++;		
		allDone = true;
		for (int i =0; i <numProc; i++){
			if (allProcessors[i].getNumProcesses()>0){
			// if any processors still have proceses, keep going
				allDone =false;
			}
					
		}
	}
	}
	
}
void Scheduler::HRN() {
	cout<<"Running Hightest Response Ratio Next..."<<endl;
	numQueues=1;
	allProcessors = new Processor[numProc];
	// add the specified number of queues to the queue holder
	for (int lcv2 =0; lcv2<numProc; lcv2++){
		queue<Process>** queueHolder = new queue<Process>*[1];
		MLqueue = new queue<Process>();
		queueHolder[0]=MLqueue;
			//Processor* p = new Processor(queueHolder);
		Processor p;
		cout<<"Numqueues in Scheduler: "<<numQueues<<endl;
		p.addQueueList(queueHolder, numQueues);
		allProcessors[lcv2]=p;
	}
	tempPQ = new priority_queue< Process, vector<Process>, less<vector<Process>::value_type> >();

	int time =0;
	bool done = false;
	bool allDone = false;
	while (allDone == false){
		cout<<"***********************************************************"<<endl;
		cout<<"Time: "<<time<<" ";
		
		
		
		// renew temp priority queue
		tempPQ = new priority_queue< Process, vector<Process>, less<vector<Process>::value_type> >();
		
		// this process should reorder the waiting processes list
		//iterate through list of waiting processes and add them to priority queue while popping them from the waiting list
		
		for (int lcv2 =0; lcv2 < numProc; lcv2++){
		cout<<endl;
	//		cout<<"Reordering processes for Processor: "<<lcv2<<endl;
			int curSize = allProcessors[lcv2].getNumWaitingProcesses();
			cout<<curSize<<endl;
			for (int lcv =0; lcv < curSize; lcv++){
				//Process tempP = waitingProcesses->front();
				Process *tempP = new Process();
				*tempP = allProcessors[lcv2].multipleQueue[0]->front();				
				tempP->waitTime = time - tempP->arrival;
				tempP->setHRNPriority();
			//	cout<<"Dumping: "<<tempP->PID;
				tempPQ->push(*tempP);
				allProcessors[lcv2].multipleQueue[0]->pop();
			
			}
			//readd processes from priority queue to the waiting list
			//int curSize = tempPQ->size();
			int size2 = tempPQ->size();
			for (int lcv =0; lcv < size2; lcv++){
				Process *curP = new Process();
				*curP = tempPQ->top();
			//	cout<<"Readding this process to waiting list: "<< curP->PID;
				tempPQ->pop();
				//waitingProcesses->push_back(curP);
				allProcessors[lcv2].multipleQueue[0]->push(*curP);
			}
		}
		
		cout<<endl;
		 if (!pq->empty()){
			while (pq->top().arrival==time&&done == false){
				
				// go through the process pq and add each process to the waiting processes list if their arrival matches the time
				cout<<"Process "<<pq->top().PID<<" arrives. "<<endl;
				/*----------------NEW MULTI PROCESSOR
						-	put in processor with least processes or first if tied
						-	add number
				
				*/
			
				int least = 0;
				for (int lcv =0; lcv < numProc; lcv++){
					if(allProcessors[lcv].getNumProcesses()<allProcessors[least].getNumProcesses()){
					least = lcv;
					}
				}
				
				//waitingProcesses->push_back(pq->top());
				// NEW instead
				allProcessors[least].addPtoQ(pq->top());
				cout<<"Adding Process: "<< pq->top().PID<<" to Processor "<< least<<endl;
				//cout << allProcessors[least].getNumProcesses() << " processes in processor " << least << endl;
				
				
				if (pq->size()==1){
					pq->pop();
					done = true;
					cout<<"Flags done"<<endl;
				}else{
					pq->pop();
				}				
			}
	
		 }
		 // loop through each processor
		 for(int lcv = 0; lcv < numProc; lcv++){
		cout << allProcessors[lcv].getNumProcesses() << " processes in processor " << lcv << endl;
		  //if the specified Processor is not empty
		 if(!allProcessors[lcv].isEmpty()){
		 	
			// check to see the 
			if (allProcessors[lcv].currentProcess.remainingTime==0){
				cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" finishes. ";	
				// process is done
				// set its end time
				allProcessors[lcv].currentProcess.setEndTime(time);
				// add the done process to the finished processes list
			
				allProcessors[lcv].finishedProcesses->push_back(allProcessors[lcv].currentProcess);
				// make the new process the next one in line
				allProcessors[lcv].currentProcess=allProcessors[lcv].multipleQueue[0]->front();
				
				// take the new running process out of the waiting list

				allProcessors[lcv].multipleQueue[0]->pop();
				// start its start time
				allProcessors[lcv].currentProcess.startTime=time;
				cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" gets the CPU ";
			}else if (allProcessors[lcv].currentProcess.PID==-1){
				allProcessors[lcv].currentProcess=allProcessors[lcv].multipleQueue[0]->front();
				// take the new running process out of the waiting list
				allProcessors[lcv].multipleQueue[0]->pop();
				// start its start time
				allProcessors[lcv].currentProcess.startTime=time;
				cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" gets the CPU ";
			
			}
		 }
		 else{
			if (allProcessors[lcv].currentProcess.remainingTime==0){
				cout<<"Process "<<allProcessors[lcv].currentProcess.PID<<" finishes. ";
				allProcessors[lcv].currentProcess.setEndTime(time);
				allProcessors[lcv].finishedProcesses->push_back(allProcessors[lcv].currentProcess);
				if (done==true){
					
					// There are no more processes in allProcessor[lcv]
					//check to see if any other processes have processes waiting and add it to that one
					bool swappedProcess = false;
					for (int lcv2 =0; lcv2<numProc; lcv2++){
						if ((lcv2 != lcv)&&(swappedProcess ==false)){
							if (allProcessors[lcv2].getNumWaitingProcesses()>0){
								cout<<"Adding Process "<<allProcessors[lcv2].multipleQueue[0]->front().PID<<" from Processor "<<lcv2 <<" to Processor "<<lcv<<endl;
								allProcessors[lcv].currentProcess =allProcessors[lcv2].multipleQueue[0]->front();
								allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime+1;
								allProcessors[lcv2].multipleQueue[0]->pop();
								swappedProcess = true;
							}
						}
					}

				}
				else{

						allProcessors[lcv].currentProcess.PID = -1;

				}
			
			}
		 }
		 if (allProcessors[lcv].currentProcess.remainingTime>0){
		 allProcessors[lcv].currentProcess.remainingTime = allProcessors[lcv].currentProcess.remainingTime-1;
		 cout<<"Remaining Time for process "<<allProcessors[lcv].currentProcess.PID<<": "<<allProcessors[lcv].currentProcess.remainingTime<<endl;
		 allProcessors[lcv].currentProcess.runTime = allProcessors[lcv].currentProcess.runTime+1;
		 }else{
			Process p;
			allProcessors[lcv].currentProcess = p;
		 }
}		 
		time++;
		cout<<endl;
		cout<<"First Process of Processor 0: "<<allProcessors[0].multipleQueue[0]->front()<<endl;
		//cout<<"First Process of Processor 1: "<<allProcessors[1].multipleQueue[0]->front()<<endl;
		cout<<endl;
							allDone = true;
					for (int i =0; i <numProc; i++){
						if (allProcessors[i].getNumProcesses()>0){
							// if any processors still have proceses, keep going
							allDone =false;
						}
					
					}
	}
	printResults();
	
	
		
	
}

void Scheduler::printResults(){
	for (int lcv =0; lcv<numProc; lcv++){
		cout<<"Processor "<< lcv<<": "<<endl;
		list<Process>::iterator i = allProcessors[lcv].finishedProcesses->begin();
		Process printProcess;
		int waitTime =0;
		int totalWait =0;
		int totalTOT =0;
		int numProcesses=0;
		int turnaroundTime=0;
		cout<<"PID Wait Turnaround"<<endl;
		while(i != allProcessors[lcv].finishedProcesses->end()){
			numProcesses++;
			printProcess= *i;
			//cout<<"PID"<<printProcess.PID<<" arrival "<<printProcess.arrival<< " burst "<< printProcess.burst<< " endtime "<< printProcess.endTime<<endl;
			waitTime = printProcess.endTime - printProcess.burst - printProcess.arrival;
			turnaroundTime = printProcess.endTime- printProcess.arrival;
			cout<<printProcess.PID<<"    "<<waitTime<<"    "<<turnaroundTime<<endl;
			i++;
			totalWait= totalWait+waitTime;
			totalTOT= totalTOT+turnaroundTime;
		}
		cout<<"Average Wait Time: "<< totalWait/numProcesses<<endl;
		cout<<"Average Turnaround Time: "<< totalTOT/numProcesses<<endl;
		printGantt(lcv);
		cout<<endl;
	}
}

void Scheduler::display(ostream& os) const{
	if (userProcess==1){
		cout<<"FCFS Scheduling"<<endl;
	}
	if (userProcess==2){
		cout<<"SRTF Scheduling"<<endl;
	}
	if (userProcess==3){
		cout<<"Priority Scheduling"<<endl;
	}
	if (userProcess==4){
		cout<<"Pre-Emptive Priority Scheduling"<<endl;
	}
	if (userProcess==5){
		cout<<"Round Robin Scheduling"<<endl;
	}
}


bool operator>(const Process &a, const Process &b){
	return (!(a.isLessThan(b)));
}

void Scheduler::printGantt(int Proc){
	list<Process>::iterator i = allProcessors[Proc].finishedProcesses->begin();
	Process printProcess;
	cout<<"Time:      ";
	while(i != allProcessors[Proc].finishedProcesses->end()){
		printProcess= *i;
		cout<<printProcess.startTime;
		for (int lcv= printProcess.startTime; lcv< printProcess.endTime; lcv++){
			cout<<"-";
		}
		i++;
	}
	cout<<endl;
	cout<<"Processes: ";
	list<Process>::iterator it = allProcessors[Proc].finishedProcesses->begin();
	Process printProcess2;
	while(it != allProcessors[Proc].finishedProcesses->end()){
		printProcess2= *it;
		cout<<printProcess2.PID<<" ";
		for (int lcv= printProcess2.startTime; lcv< printProcess2.endTime; lcv++){
			cout<<" ";
		}
		it++;
	}



}

#endif


